# ExtraWarnings.cmake

# https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html

# 检查是否已经包含了这个模块，防止重复包含
if(DEFINED EXTRA_WARNINGS_INCLUDED)
    return()
endif()
set(EXTRA_WARNINGS_INCLUDED TRUE)

# Sanitizer 选项
option(ENABLE_ASAN "Enable AddressSanitizer" OFF)
option(ENABLE_MSAN "Enable MemorySanitizer" OFF)
option(ENABLE_TSAN "Enable ThreadSanitizer" OFF)
option(ENABLE_UBSAN "Enable Undefined Behavior Sanitizer" OFF)
option(ENABLE_LSAN "Enable Leak Sanitizer" OFF)

function(apply_extra_warnings_to_target TARGET_NAME)
    # 定义公共的警告标志
    set(COMMON_WARNING_FLAGS
        "-Wextra"                           # 启用额外的警告
        "-Wpedantic"                        # 严格遵循 ISO C/C++ 标准
        "-Wconversion"                      # 警告隐式类型转换
        "-Wsign-compare"                    # 警告有符号和无符号类型的比较
        "-Wzero-as-null-pointer-constant"   # 警告使用 0 作为空指针常量
        "-Wmissing-declarations"            # 警告缺少函数声明
        "-Wold-style-cast"                  # 警告 C 风格的类型转换 (仅 C++)
        "-Wshadow"                          # 警告变量覆盖
        "-Wwrite-strings"                   # 将字符串常量视为 const char*
        "-Wformat=2"                        # 检查 printf/scanf/... 格式字符串
        "-Wundef"                           # 警告未定义的宏在 #if 中被评估
        "-Wlogical-op"                      # 警告可疑的逻辑运算
        "-Wmissing-include-dirs"            # 警告缺少用户指定的包含目录
        "-Wredundant-decls"                 # 警告冗余声明
        "-Wstrict-overflow=5"               # 假设有符号溢出是未定义的
        "-Wswitch-default"                  # 警告 switch 语句缺少 default 分支
        "-Wswitch-enum"                     # 警告 switch 未覆盖所有枚举值
        "-Wno-variadic-macros"              # 允许使用可变参数宏
        "-Wcast-qual"                       # 警告丢弃类型限定符的转换
        "-Wdisabled-optimization"           # 警告无法优化的函数
        "-Wdouble-promotion"                # 警告 float 到 double 的隐式转换
        "-Wformat-signedness"               # 警告 printf/scanf 中的符号不匹配
        "-Wtrampolines"                     # 警告需要可执行栈的代码生成
        "-Wunused-macros"                   # 警告未使用的宏
        "-Wmissing-format-attribute"        # 警告应声明为 format 函数的函数
        "-Wdeprecated-declarations"         # 警告使用废弃的函数或类型
        "-Wuninitialized"                   # 警告可能未初始化的变量使用
        "-Wmaybe-uninitialized"             # GCC 报告更多潜在的未初始化变量使用情况
        # "-Wsuggest-attribute=pure"          # GCC 建议使用 pure 属性
        # "-Wsuggest-attribute=const"         # GCC 建议使用 const 属性
        "-Wsequence-point"                  # 检测可能导致未定义行为的代码，特别是涉及到序列点（sequence points）的问题
        # "-Wreturn-type"                     # 警告函数返回类型不一致的情况 -Wall
        "-fstack-protector-strong"          # 启用堆栈保护, 测到栈破坏时，它通常会导致程序立即终止
        "-fno-common"                       # 将未初始化的全局变量放入 .bss 段
        # "-fvisibility=hidden"               # 默认隐藏符号可见性, 对于旧代码可能会导致符号缺失
        "-D_FORTIFY_SOURCE=2"               # 启用一些额外的运行时检查，如缓冲区溢出检查
    )

    # 检查编译器是否支持 -march=native
    include(CheckCXXCompilerFlag)
    check_cxx_compiler_flag("-march=native" COMPILER_SUPPORTS_MARCH_NATIVE)
    if(COMPILER_SUPPORTS_MARCH_NATIVE)
        list(APPEND COMMON_WARNING_FLAGS "-march=native")  # 针对本机架构优化
    endif()

    # 设置 C 编译器标志
    target_compile_options(${TARGET_NAME} PRIVATE
        $<$<COMPILE_LANGUAGE:C>:${COMMON_WARNING_FLAGS}>
    )

    # 设置 C++ 编译器标志
    set(CXX_SPECIFIC_FLAGS
        "-Wnon-virtual-dtor"            # 警告非虚析构函数
        "-Weffc++"                      # 警告违反 Scott Meyers 的 "Effective C++" 准则
        "-Wpointer-arith"               # 警告无效的指针运算
        "-Wctor-dtor-privacy"           # 警告类中缺少构造函数
        "-Woverloaded-virtual"          # 警告方法隐藏而非重写
        "-Wstrict-null-sentinel"        # 警告不正确的空指针检查
        "-Wnoexcept"                    # C++11 警告可能抛出异常的 noexcept 函数
        "-Wsuggest-override"            # C++11 建议使用 override 关键字
    )
    target_compile_options(${TARGET_NAME} PRIVATE
        $<$<COMPILE_LANGUAGE:CXX>:${COMMON_WARNING_FLAGS} ${CXX_SPECIFIC_FLAGS}>
    )

    # 设置链接器标志
    target_link_options(${TARGET_NAME} PRIVATE
        -rdynamic
        -Wl,--build-id=sha1
    )

    # Sanitizer 选项
    if(CMAKE_BUILD_TYPE STREQUAL "Debug")
        if(ENABLE_ASAN)
            set(SANITIZE_FLAGS "-fsanitize=address")
        elseif(ENABLE_MSAN)
            set(SANITIZE_FLAGS "-fsanitize=memory")
        elseif(ENABLE_TSAN)
            set(SANITIZE_FLAGS "-fsanitize=thread")
        elseif(ENABLE_LSAN)
            set(SANITIZE_FLAGS "-fsanitize=leak")
        endif()

        # 可以单独启用 UBSan，或与其他 sanitizer 组合使用
        if(ENABLE_UBSAN OR (SANITIZE_FLAGS AND NOT ENABLE_MSAN))
            set(SANITIZE_FLAGS "${SANITIZE_FLAGS},undefined")
        endif()

        if(SANITIZE_FLAGS)
            # 添加通用的 sanitizer 选项
            set(SANITIZE_FLAGS "${SANITIZE_FLAGS} -fno-omit-frame-pointer -fno-optimize-sibling-calls")
            
            # 应用 sanitizer 标志到编译和链接标志
            target_compile_options(${TARGET_NAME} PRIVATE ${SANITIZE_FLAGS})
            target_link_options(${TARGET_NAME} PRIVATE ${SANITIZE_FLAGS})

            # 输出启用的 sanitizer 信息
            message(STATUS "Enabled sanitizers for ${TARGET_NAME} in Debug build: ${SANITIZE_FLAGS}")
        endif()
    else()
        if(ENABLE_ASAN OR ENABLE_MSAN OR ENABLE_TSAN OR ENABLE_UBSAN OR ENABLE_LSAN)
            message(WARNING "Sanitizers are only enabled in Debug mode for ${TARGET_NAME}. Current build type: ${CMAKE_BUILD_TYPE}")
        endif()
    endif()

    # 通知用户
    get_target_property(TARGET_C_FLAGS ${TARGET_NAME} COMPILE_OPTIONS)
    message(STATUS "Extra warnings enabled for ${TARGET_NAME}: ${TARGET_C_FLAGS}\n")
endfunction()